/*
 * $Id: stdlib_base64.c,v 1.2 2006-03-11 12:31:10 bacon Exp $
 */

#include <xp/bas/stdlib.h>

#define ENC(x) \
	((x < 26)? ('A' + x): \
	 (x < 52)? ('a' + (x - 26)): \
	 (x < 62)? ('0' + (x - 52)): \
	 (x == 62)? ('+'): ('/'))

#define DEC(x) \
	((x >= 'A' && x <= 'Z')? (x - 'A'): \
	 (x >= 'a' && x <= 'z')? (x - 'a' + 26): \
	 (x >= '0' && x <= '9')? (x - '0' + 52): \
	 (x == '+')? 62: 63)

xp_size_t xp_encodebase64 (
	const xp_byte_t* in, xp_size_t isz,
	xp_byte_t* out, xp_size_t osz, xp_size_t* xsz)
{
	xp_size_t idx = 0, idx2 = 0, i;

	/* 3 8-bit values to 4 6-bit values */

	for (i = 0; i < isz; i += 3) {
		xp_byte_t b1, b2, b3;
		xp_byte_t c1, c2, c3, c4;

		b1 = in[i];
		b2 = (i + 1 < isz)? in[i + 1]: 0;
		b3 = (i + 2 < isz)? in[i + 2]: 0;

		c1 = b1 >> 2;
		c2 = ((b1 & 0x03) << 4) | (b2 >> 4);
		c3 = ((b2 & 0x0F) << 2) | (b3 >> 6);
		c4 = b3 & 0x3F;

		if (idx + 3 < osz) {
			out[idx++] = ENC(c1);
			out[idx++] = ENC(c2);
			out[idx++] = (i + 1 < isz)? ENC(c3): '=';
			out[idx++] = (i + 2 < isz)? ENC(c4): '=';
		}
		idx2 += 4;
	}

	if (xsz != XP_NULL) *xsz = idx2;
	return idx;
}

xp_size_t xp_decodebase64 (
	const xp_byte_t* in, xp_size_t isz,
	xp_byte_t* out, xp_size_t osz, xp_size_t* xsz)
{
	xp_size_t idx = 0, idx2 = 0, i;

	for (i = 0; i < isz; i += 4)  {
		xp_byte_t c1, c2, c3, c4;
		xp_byte_t b1, b2, b3, b4;

		c1 = in[i];
		c2 = (i + 1 < isz)? in[i + 1]: 'A';
		c3 = (i + 2 < isz)? in[i + 2]: 'A';
		c4 = (i + 3 < isz)? in[i + 3]: 'A';

		b1 = DEC(c1);
		b2 = DEC(c2);
		b3 = DEC(c3);
		b4 = DEC(c4);

		idx2++;
		if (idx < osz) out[idx++] = (b1 << 2) | (b2 >> 4);

		if (c3 != '=') {
			idx2++;	
			if (idx < osz) out[idx++] = ((b2 & 0x0F) << 4) | (b3 >> 2);
		}
		if (c4 != '=') {
			idx2++;	
			if (idx < osz) out[idx++] = ((b3 & 0x03) << 6) | b4;
		}
	}

	if (xsz != XP_NULL) *xsz = idx2;
	return idx;
}
